package com.youi.third.wechat.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;


import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.codehaus.xfire.util.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;

import com.youi.third.wechat.model.AccessToken;
import com.youi.third.wechat.model.BaseObject;
import com.youi.third.wechat.model.WaChatConfig;
import com.youi.third.wechat.model.WaChatOrderPayObject;
import com.youi.third.wechat.model.WeChatMiniUserInfo;
import com.youi.third.wechat.model.WeChatUserInfo;
import com.youi.util.DateUtil;
import com.youi.util.JsonUtil;
import com.youi.util.LogUtil;
import com.youi.util.MD5;

/**
 * 微信公众平台工具类
 * @author erdong.Chan
 *
 */
public class WechatUtil {
	

	public static String key=null;		//微信商户平台(pay.weixin.qq.com)→账户设置 →API安全→ 密钥设置
	public static String notifyUrl=null;		//支付成功回调地址
	
	public static String phone_appid=null;		//app支付用 appid
	public static String phone_mch_id=null;		//app支付用 商户号
	public static String phone_secret=null;		//app密钥AppSecret

	public static String wechat_appid=null;		//公众号支付用 appid
	public static String wechat_mch_id=null;	//公众号支付用 商户号
	public static String wechat_secret=null;	//公众号支付secret
	
	public static String web_appid=null;		//网页授权应用id
	public static String web_secret=null;		///网页授权密钥
	
	public static String mini_appid=null;		//小程序授权应用id
	public static String mini_secret=null;		//小程序密钥
	public static String mini_mch_id=null;		//小程序支付用 商户号
	
	private static Random random=new Random();	//随机数
	private static AccessToken  ticketAccessToken;
	
	
	/**
	 * 加载配置文件
	 */
	static{
        Properties prop = new Properties();
        InputStream in = WechatUtil.class.getResourceAsStream("/wechat.properties");
		try {
		    prop.load(in);
		    String prfix="wechat.";//前缀
			key = prop.getProperty(prfix+"key").trim();
			notifyUrl = prop.getProperty(prfix+"notifyUrl").trim();
			phone_appid = prop.getProperty(prfix+"phone_appid").trim();
			phone_mch_id = prop.getProperty(prfix+"phone_mch_id").trim();
			phone_secret = prop.getProperty(prfix+"phone_secret").trim();
			wechat_appid = prop.getProperty(prfix+"wechat_appid").trim();
			wechat_mch_id = prop.getProperty(prfix+"wechat_mch_id").trim();
			wechat_secret = prop.getProperty(prfix+"wechat_secret").trim();
			web_appid = prop.getProperty(prfix+"web_appid").trim();
			web_secret = prop.getProperty(prfix+"web_secret").trim();
			mini_appid = prop.getProperty(prfix+"mini_appid").trim();
			mini_secret = prop.getProperty(prfix+"mini_secret").trim();
			mini_mch_id = prop.getProperty(prfix+"mini_mch_id").trim();
	    } catch (IOException e) {
	        e.printStackTrace();
	    }
	}
	
	public  WechatUtil(String phone_appid,String phone_mch_id,String phone_secret,String wechat_appid,
			String wechat_mch_id,String wechat_secret,String key,String notifyUrl){
		this.phone_appid=phone_appid;
		this.phone_mch_id=phone_mch_id;
		this.phone_secret=phone_secret;
		this.wechat_appid=wechat_appid;
		this.wechat_mch_id=wechat_mch_id;
		this.wechat_secret=wechat_secret;
		this.key=key;
		this.notifyUrl=notifyUrl;
	}
	
	
	/**
	 * app授权 获取用户openid和session_key
	 * @param code
	 * @return
	 */
	public static BaseObject appUserGetBaseObject(String code){
		LogUtil.Info("开始APP用户授权根据code获取OpenId code="+code);
		if(code!=null&&phone_appid!=null&&code!=null){
			String urlStr="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+phone_appid+"&secret="+phone_secret+"&code="+code+"&grant_type=authorization_code";
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "APP用户授权根据code获取OpenId");
			if(responseStr.contains("openid")){
	        	BaseObject baseObject=JsonUtil.fromJson(responseStr, BaseObject.class);
	        	return baseObject;
	        }
		}
		return null;	
	}
	
	/**
	 * 公众号授权根据code获取OpenId
	 * @param code
	 * @return
	 */
	public static BaseObject getBaseObject(String code){
		LogUtil.Info("开始公众号授权根据code获取OpenId code="+code);
		if(code!=null&&wechat_appid!=null&&code!=null){
			String urlStr="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+wechat_appid+"&secret="+wechat_secret+"&code="+code+"&grant_type=authorization_code";
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "公众号授权根据code获取OpenId");
			if(responseStr.contains("openid")){
	        	BaseObject baseObject=JsonUtil.fromJson(responseStr, BaseObject.class);
	        	return baseObject;
	        }
		}
		return null;	
	}
	
	/**
	 * 公众号获取接口调用凭证AccessToken(非获取用户数据的AccessToken)
	 * @return
	 */
	public static String getAccessToken(){
		String urlStr="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+wechat_appid+"&secret="+wechat_secret;
		String responseStr=wechatHttpUtil(urlStr, "GET", null, "公众号获取接口调用凭证AccessToken(非获取用户数据的AccessToken)");
        if(responseStr.contains("access_token")){
        	AccessToken accessToken=JsonUtil.fromJson(responseStr, AccessToken.class);
        	return accessToken.getAccess_token();
        }else {
        	return null;
		}
	}
	
	/**
	 * 小程序获取接口调用凭证AccessToken
	 * @return
	 */
	public static AccessToken weChatMiniGetAccessToken(){
		String urlStr="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+mini_appid+"&secret="+mini_secret;
		String responseStr=wechatHttpUtil(urlStr, "GET", null, "小程序获取AccessToken");
		if(responseStr.contains("access_token")){
        	AccessToken accessToken=JsonUtil.fromJson(responseStr, AccessToken.class);
        	return accessToken;
        }else {
        	return null;
		}
	}
	
	/**
	 * 小程序授权 获取用户openid和session_key’ 
	 * @return
	 */
	public static BaseObject weChatMiniGetBaseObject(String code){
		LogUtil.Info("开始程序授权 获取用户openid和session_key code="+code);
		String urlStr="https://api.weixin.qq.com/sns/jscode2session?appid="+mini_appid+"&secret="+mini_secret+"&js_code="+code+"&grant_type=authorization_code";
		String responseStr=wechatHttpUtil(urlStr, "GET", null, "小程序获取用户openid");
		if(responseStr.contains("openid")){
			if(responseStr.contains("openid")){
				BaseObject baseObject=JsonUtil.fromJson(responseStr.toString(), BaseObject.class); 
	        	return baseObject;
	        }
        }
		return null;
	}
	
	/**
	 * 网页微信授权登录获取AccessToken
	 * @return
	 */
	public static AccessToken weChatLicenseLoginGetAccessToken(String code){
		LogUtil.Info("开始网页微信授权登录获取AccessToken code="+code);
		if (code!=null) {
			String urlStr="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+web_appid+"&secret="+web_secret+"&code="+code+"&grant_type=authorization_code";
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "网页微信授权登录获取AccessToken");
			if(responseStr.contains("access_token")){
	        	AccessToken accessToken=JsonUtil.fromJson(responseStr, AccessToken.class);
	        	return accessToken;
	        }
		}
		return null;
	}
	
	/**
	 * 网页微信授权登录获取userinfo(UnionID机制)
	 * @return
	 */
	public static String weChatLicenseLoginGetUserinfo(String access_token,String openid){
		LogUtil.Info("开始网页微信授权登录获取用户信息:openid="+openid+",access_token="+access_token);
		if (access_token!=null&&openid!=null) {
			String urlStr="https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid;
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "网页微信授权登录获取userinfo(UnionID机制)");
			if(responseStr.contains("access_token")){
	        	AccessToken accessToken=JsonUtil.fromJson(responseStr, AccessToken.class);
	        	return accessToken.getAccess_token();
	        }
		}
		return null;
	}
	
	/**
	 * 获取用户信息(需要access_token)
	 * @param openid
	 * @param access_token
	 * @return
	 */
	public static WeChatUserInfo getUserInfo(String openid,String access_token){
		LogUtil.Info("开始获取用户信息:openid="+openid+",access_token="+access_token);
		if(openid!=null&&access_token!=null){
			String urlStr="https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid+"&lang=zh_CN ";
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "获取用户信息(需要access_token)");
			if(responseStr.contains("openid")){
	        	WeChatUserInfo userInfo=JsonUtil.fromJson(responseStr, WeChatUserInfo.class);
	        	if (userInfo!=null) {
	        		LogUtil.Info("获取用户信息成功:"+userInfo.toString());
				}
	        	return userInfo;
	        }
		}
		return null;
	}
	
	/**
	 * 获取用户基本信息（包括UnionID机制）
	 * @return
	 */
	public static WeChatUserInfo getUserInfo(String openid){
		LogUtil.Info("开始获取用户信息:openid="+openid);
		if (openid!=null) {
			String urlStr="https://api.weixin.qq.com/cgi-bin/user/info?access_token="+getAccessToken()+"&openid="+openid+"&lang=zh_CN";
			String responseStr=wechatHttpUtil(urlStr, "GET", null, "获取用户基本信息（包括UnionID机制）");
			if(responseStr.contains("openid")){
				WeChatUserInfo userInfo=JsonUtil.fromJson(responseStr, WeChatUserInfo.class);
	        	if (userInfo!=null) {
	        		LogUtil.Info("获取用户信息成功:"+userInfo.toString());
	        		return userInfo;
				}
	        }
		}
		return null;
	}
	
	/**
	 * 公众号支付 统一下单获取prepay_id
	 * @param outTradeNo
	 * @param ip
	 * @param openid
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayId(String outTradeNo,String ip,String openid,String body,String notifyUrl,Integer totalFee){
		return getPrepayIdByType(0, outTradeNo, ip, body, notifyUrl, totalFee,openid);
	}
	
	/**
	 * 扫码支付 统一下单获取prepay_id
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayIdByScanner(String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee){
		return getPrepayIdByType(1, outTradeNo, ip, body, notifyUrl, totalFee,null);
	}

	/**
	 * APP支付 统一下单获取prepay_id
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayIdByAPP(String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee){
		return getPrepayIdByType(2, outTradeNo, ip, body, notifyUrl, totalFee,null);
	}
	
	/**
	 * H5支付 统一下单获取prepay_id
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayIdByH5(String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee){
		return getPrepayIdByType(3, outTradeNo, ip, body, notifyUrl, totalFee,null);
	}
	
	/**
	 * 小程序支付 统一下单获取prepay_id
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayIdByMini(String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee,String openid){
		return getPrepayIdByType(4, outTradeNo, ip, body, notifyUrl, totalFee,openid);
	}
	
	/**
	 * 微信支付统一下单根据类型
	 * @param type   0  公众号支付    1 扫码支付       2 APP支付      3 H5支付    4小程序
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayIdByType(Integer type,String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee,String openid){
		if (type!=null) {
			switch ((int)type) {
				case 0:
					String responseStr=getPrepayId(wechat_appid, wechat_mch_id, key, "JSAPI", outTradeNo, ip, body, notifyUrl, totalFee, "公众号",openid);
					if(responseStr.contains("SUCCESS")){
				        LogUtil.Info("发起统一下单获取prepay_id成功");
				        return WeChatXmlUtil.readXmlGetPms(responseStr, "prepay_id");	//获取prepay_id
				    }
					break;
				case 1:
					responseStr=getPrepayId(phone_appid, phone_mch_id, key, "NATIVE", outTradeNo, ip, body, notifyUrl, totalFee, "扫码",openid);
					if(responseStr.contains("SUCCESS")){
			        	LogUtil.Info("发起统一下单获取数据成功");
			        	WaChatOrderPayObject  waChatOrderPayObject=WeChatXmlUtil.readXmlGetOderPayObject(responseStr);	
			        	if (waChatOrderPayObject.getCode_url()!=null) {
			        		LogUtil.Info("waChatOrderPayObject的数据为="+waChatOrderPayObject.toString());
			        		return waChatOrderPayObject.getCode_url();
						}
			        }
					break;
				case 2:
					responseStr=getPrepayId(phone_appid, phone_mch_id, key, "APP", outTradeNo, ip, body, notifyUrl, totalFee, "APP",openid);
					if(responseStr.contains("SUCCESS")){
			        	LogUtil.Info("发起统一下单获取数据成功");
			        	WaChatOrderPayObject  waChatOrderPayObject=WeChatXmlUtil.readXmlGetOderPayObject(responseStr);	
			        	LogUtil.Info("APPwaChatOrderPayObject的数据为="+waChatOrderPayObject.toString());
			        	if (waChatOrderPayObject!=null&&waChatOrderPayObject.getPrepay_id()!=null) {
			        		//return getAppPrepayIdSign(waChatOrderPayObject.getPrepay_id());//获取统一下单id后生成签名
			        		return waChatOrderPayObject.getPrepay_id();//获取统一下单id后生成签名
						}
			        }
					break;
				case 3:
					responseStr=getPrepayId(phone_appid, phone_mch_id, key, "MWEB", outTradeNo, ip, body, notifyUrl, totalFee, "H5",openid);
					if(responseStr.contains("SUCCESS")){
			        	LogUtil.Info("发起统一下单获取数据成功");
			        	WaChatOrderPayObject  waChatOrderPayObject=WeChatXmlUtil.readXmlGetOderPayObject(responseStr);	
			        	LogUtil.Info("waChatOrderPayObject的数据为="+waChatOrderPayObject.toString());
			        	if (waChatOrderPayObject!=null&&waChatOrderPayObject.getMweb_url()!=null) {
			        		return waChatOrderPayObject.getMweb_url();//获取统一下单id后生成签名
						}
			        }
					break;
				case 4:
					responseStr=getPrepayId(mini_appid, mini_mch_id, key, "JSAPI", outTradeNo, ip, body, notifyUrl, totalFee, "小程序",openid);
					if(responseStr.contains("SUCCESS")){
				        LogUtil.Info("发起统一下单获取prepay_id成功");
				        return WeChatXmlUtil.readXmlGetPms(responseStr, "prepay_id");	//获取prepay_id
				    }
					break;
			}
		}
		return null;
	}
	
	/**
	 * 微信支付统一下单
	 * @param appid
	 * @param mch_id
	 * @param key
	 * @param trade_type
	 * @param outTradeNo
	 * @param ip
	 * @param body
	 * @param notifyUrl
	 * @param totalFee
	 * @return
	 */
	public static String getPrepayId(String appid,String mch_id,String key,String trade_type,String outTradeNo,String ip,String body,String notifyUrl,Integer totalFee,String memo,String openid){
		String nonceStr=getNonceStr(10);//随机字符串
		String sintrt="appid="+appid+"&body="+body+"&mch_id="+mch_id+"&nonce_str="+nonceStr+"&notify_url="+notifyUrl+"OPENID&out_trade_no="+outTradeNo+"&spbill_create_ip="+ip+"&total_fee="+totalFee+"&trade_type="+trade_type+"&key="+key;
		if (openid!=null) {
			sintrt=sintrt.replace("OPENID", "&openid="+openid);
		}else {
			sintrt=sintrt.replace("OPENID", "");
		}
		String sign=MD5.getMD5(sintrt).toUpperCase();
        String params="<xml>"+
						"<appid>"+appid+"</appid>"+
						"<body>"+body+"</body>"+
						"<mch_id>"+mch_id+"</mch_id>"+
						"<nonce_str>"+nonceStr+"</nonce_str>"+
						"OPENID"+
						"<notify_url>"+notifyUrl+"</notify_url>"+
						"<out_trade_no>"+outTradeNo+"</out_trade_no>"+
						"<spbill_create_ip>"+ip+"</spbill_create_ip>"+
						"<total_fee>"+totalFee+"</total_fee>"+
						"<trade_type>"+trade_type+"</trade_type>"+
						"<sign>"+sign+"</sign>"+
					"</xml>";
        if (openid!=null) {
        	params=params.replace("OPENID", "<openid>"+openid+"</openid>");
		}else {
			params=params.replace("OPENID", "");
		}
		return getPrepayIdRequest(params, memo);
	}
	
	/**
	 * 
	 * 微信支付统一下单请求
	 * @param params
	 * @param memo
	 * @return
	 */
	public static String getPrepayIdRequest(String params,String memo){
		if (params!=null&&params.length()>0) {
			LogUtil.Info("开始发起微信"+memo+"支付统一下单");
			try {
				String urlStr="https://api.mch.weixin.qq.com/pay/unifiedorder";
				URL url=new URL(urlStr);
				HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
				urlConnection.setDoOutput(true);
				urlConnection.setDoInput(true);
				urlConnection.setRequestMethod("POST");
				urlConnection.setUseCaches(false); //Post 请求不能使用缓存
				urlConnection.setInstanceFollowRedirects(true);
				urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
				urlConnection.setRequestProperty("Charset", "UTF-8"); 
				urlConnection.connect();
				DataOutputStream out = new DataOutputStream(urlConnection.getOutputStream());
		        params=new String(params.getBytes("UTF-8"), "ISO-8859-1");//utf-8编码
		        out.writeBytes(params);
		        out.flush();
		        out.close(); 
		        BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(),"UTF-8"));
		        String line;
		        String responseStr="";
		        while ((line = reader.readLine()) != null){
		        	responseStr+=line;
		        }
		        reader.close();
		        urlConnection.disconnect();
		        LogUtil.Info("统一下单返回信息:"+responseStr);
		        return responseStr;
			} catch (IOException e) {
				e.printStackTrace();
				LogUtil.Info("发起微信"+memo+"支付统一下单出现异常");
				return null;
			}
		}
		return null;
	}
	
	/**
	 * 获取app统一下单的签名
	 * @param prepayid
	 * @return
	 */
	public static String getAppPrepayIdSign(String prepayid){
		if (prepayid!=null) {
			LogUtil.Info("开始生成app支付签名");
			String sintrt="appid="+phone_appid+"&partnerid="+phone_mch_id+"&prepayid="+prepayid+"&package=Sign=WXPay"+"&noncestr="+getNonceStr(10)+"&timestamp="+new Date().getTime()+"&key="+key;
			LogUtil.Info("签名前数据为="+sintrt);
			String sign=MD5.getMD5(sintrt).toUpperCase();
			LogUtil.Info("生成app支付签名结束");
			return sign;
		}
		return null;
	}
	
	/**
	 * 根据支付类型查询订单支付状态
	 * @param type	0  公众号支付    1 扫码支付       2 APP支付      3 H5支付  
	 * @param out_trade_no	订单号
	 * @return
	 */
	public static WaChatOrderPayObject getOrderPayStateByPayType(Integer type,String out_trade_no){
		if (type!=null) {
			if (type==0) {
				return getOrderPayState(wechat_appid, wechat_mch_id, key, out_trade_no);
			}else if (type==1||type==2||type==3) {
				return getOrderPayState(phone_appid, phone_mch_id, key, out_trade_no);
			}
		}
		return null;
	}
	
	/**
	 * 查询APP订单支付状态
	 * @param out_trade_no
	 * @return
	 */
	public static WaChatOrderPayObject getOrderPayStateByApp(String out_trade_no){
		return getOrderPayStateByPayType(1, out_trade_no);
	}
	
	/**
	 * 查询微信公众号订单支付状态
	 * @param out_trade_no
	 * @return
	 */
	public static WaChatOrderPayObject getOrderPayStateByWeChat(String out_trade_no){
		return getOrderPayStateByPayType(0, out_trade_no);
	}
	
	/**
	 * 查询订单支付状态
	 * @param out_trade_no
	 * @return
	 */
	public static WaChatOrderPayObject getOrderPayState(String appid,String mchid,String key,String out_trade_no){
		if (mchid!=null&&mchid.length()>0&&appid!=null&&appid.length()>0) {
			LogUtil.Info("开始支付状态查询 out_trade_no="+out_trade_no);
			String urlStr="https://api.mch.weixin.qq.com/pay/orderquery";
			String nonceStr=getNonceStr(10);
			String sintrt="appid="+appid+"&mch_id="+mchid+"&nonce_str="+nonceStr+"&out_trade_no="+out_trade_no+"&key="+key;
			String sign=MD5.getMD5(sintrt).toUpperCase();
	        String params="<xml>"+
							"<appid>"+appid+"</appid>"+
							"<mch_id>"+mchid+"</mch_id>"+
							"<nonce_str>"+nonceStr+"</nonce_str>"+
							"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
							"<sign>"+sign+"</sign>"+
						  "</xml>";
			String responseStr=wechatHttpUtil(urlStr, "POST", params, "查询app订单支付状态");
			if(responseStr.contains("SUCCESS")){
		        LogUtil.Info("发起支付状态查询成功");
		        LogUtil.Info("返回数据"+responseStr);
		        return WeChatXmlUtil.readXmlGetOderPayObject(responseStr);	
		    }
		}
		return null;
	}
	
	
	/**
	 * 微信退款
	 * @param type 			0=APP、WAP退款  1 公众号 
	 * @param out_trade_no  商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
	 * @param out_refund_no 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
	 * @param total_fee		订单总金额，单位为分，只能为整数
	 * @param refund_fee	退款总金额，订单总金额，单位为分，只能为整数
	 * @param refund_desc	若商户传入，会在下发给用户的退款消息中体现退款原因
	 * @return
	 * @throws Exception 
	 */
	public static boolean payRefund(int type,String out_trade_no,String out_refund_no,int total_fee, int refund_fee,String refund_desc){
		if (type==0) {
			return payRefund(phone_mch_id, phone_appid, out_trade_no, out_refund_no, total_fee, refund_fee, refund_desc);
		}else if (type==1){
			return payRefund(wechat_mch_id, web_appid, out_trade_no, out_refund_no, total_fee, refund_fee, refund_desc);
		}
		return false;
	}
	
	/**
	 * 微信退款
	 * @param out_trade_no  商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
	 * @param out_refund_no 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
	 * @param total_fee		订单总金额，单位为分，只能为整数
	 * @param refund_fee	退款总金额，订单总金额，单位为分，只能为整数
	 * @param refund_desc	若商户传入，会在下发给用户的退款消息中体现退款原因
	 * @return
	 * @throws Exception 
	 */
	public static boolean payRefund(String mch_id,String appid,String out_trade_no,String out_refund_no,int total_fee, int refund_fee,String refund_desc){
		if (mch_id!=null&&mch_id.length()>0&&appid!=null&&appid.length()>0) {
			LogUtil.Info("开始微信退款 out_trade_no="+out_trade_no+" out_refund_no="+out_refund_no+" total_fee="+total_fee+" refund_fee="+refund_fee+" refund_desc="+refund_desc);
			try {
				/**双向认证*/
				KeyStore keyStore = KeyStore.getInstance("PKCS12");
				String filePath=WechatUtil.class.getResource("/").getPath()+"apiclient_cert.p12";
				FileInputStream instream = new FileInputStream(new File(filePath));//P12文件目录  
	            keyStore.load(instream, mch_id.toCharArray());//这里写密码..默认是你的MCHID  
		        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,phone_mch_id.toCharArray()).build();//这里也是写密码的    
		        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
		        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();  
		        instream.close(); 

		        String urlStr="https://api.mch.weixin.qq.com/secapi/pay/refund";
				String nonceStr=getNonceStr(10);
				String sintrt="appid="+appid+"&mch_id="+phone_mch_id+"&nonce_str="+nonceStr+"&out_refund_no="+out_refund_no+"&out_trade_no="+out_trade_no+"&refund_desc="+refund_desc+"&refund_fee="+refund_fee+"&total_fee="+total_fee+"&key="+key;
				String sign=MD5.getMD5(sintrt).toUpperCase();
		        String params="<xml>"+
								"<appid>"+appid+"</appid>"+
								"<mch_id>"+mch_id+"</mch_id>"+
								"<nonce_str>"+nonceStr+"</nonce_str>"+
								"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
								"<out_refund_no>"+out_refund_no+"</out_refund_no>"+
								"<total_fee>"+total_fee+"</total_fee>"+
								"<refund_fee>"+refund_fee+"</refund_fee>"+
								"<refund_desc>"+refund_desc+"</refund_desc>"+
								"<sign>"+sign+"</sign>"+
							"</xml>";		
		        HttpPost httpost = new HttpPost(urlStr); // 设置响应头信息  
	            httpost.addHeader("Connection", "keep-alive");  
	            httpost.addHeader("Accept", "*/*");  
	            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");  
	            httpost.addHeader("Host", "api.mch.weixin.qq.com");  
	            httpost.addHeader("X-Requested-With", "XMLHttpRequest");  
	            httpost.addHeader("Cache-Control", "max-age=0");  
	            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");  
	            httpost.setEntity(new StringEntity(params, "UTF-8"));  
	            CloseableHttpResponse response = httpclient.execute(httpost);  
                String responseStr = EntityUtils.toString(response.getEntity(), "UTF-8");  
                response.close(); 
                httpclient.close();
                LogUtil.Info("返回数据"+responseStr);
                String result_code=WeChatXmlUtil.readXmlGetPms(responseStr, "result_code");
                if(result_code!=null&&result_code.equals("SUCCESS")){
		        	LogUtil.Info("发起微信退款成功");
		        	return true;	
		        }else {
		        	LogUtil.Info("发起微信退款失败");
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				LogUtil.Info("发起微信退款异常");
				return false;
			} 
		}
		return false;
	}
	
	/**
	 * 查询客服消息
	 * @param access_token
	 * @return
	 */
	public static String getKeFuInfoList(String access_token){
		LogUtil.Info("开始获取客服列表信息 access_token="+access_token);
		if(access_token!=null&&access_token.length()>0){
			String urlStr="https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token="+access_token;
			return wechatHttpUtil(urlStr, "GET",null,"获取客服列表");
		}
		return null;
	}

	/**
	 * 创建客服会话
	 * @param access_token
	 * @param kf_account
	 * @param openId
	 * @return
	 */
	public static String createKfSession(String access_token,String kf_account,String openId){
		if (access_token!=null&&kf_account!=null&&openId!=null) {
			String urlStr="https://api.weixin.qq.com/customservice/kfsession/create?access_token="+access_token;
			String params="{"+
					"\"kf_account\":\""+kf_account+"\","+
					"\"openid\":\""+openId+"\""+
					"}";	
			return wechatHttpUtil(urlStr, "POST",params,"创建客服会话");
		}
		return null;
	}
	
	/**
	 * 公共请求响应工具
	 * @param urlStr
	 * @param method
	 * @return
	 */
	public static String wechatHttpUtil(String urlStr,String method,String params,String memo){
		LogUtil.Info("响应url"+urlStr);
		LogUtil.Info("开始"+memo);
		LogUtil.Info("params="+params);
		try {
			URL url = new URL(urlStr);
			HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
			urlConnection.setDoOutput(true);
			urlConnection.setDoInput(true);
			urlConnection.setRequestMethod(method);
			urlConnection.setUseCaches(false); 
			urlConnection.setInstanceFollowRedirects(true);
			urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
			urlConnection.setRequestProperty("Charset", "UTF-8"); 
			urlConnection.connect();
			DataOutputStream out = new DataOutputStream(urlConnection.getOutputStream());
			if (params!=null&&params.length()>0) {
				out.write(params.getBytes("UTF-8"));
			}
	        out.flush();
	        out.close(); 
	        BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(),"UTF-8"));
	        String line;
	        String responseStr="";
	        while ((line = reader.readLine()) != null){
	        	responseStr+=line;
	        }
	        reader.close();
	        urlConnection.disconnect();
	        LogUtil.Info("postresult="+responseStr);
	        return responseStr;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			LogUtil.Info(memo+"异常");
			LogUtil.Info(e.toString());
			return null;
		}
	}
	
	
	/**
	 * 获取微信签名配置信息
	 * @param url
	 * @return
	 */
	public  static WaChatConfig getWeChatConfig(String url){
		WaChatConfig waChatConfig=null;
		if (url!=null) {
			String ticket=getTicket();
			if (ticket!=null) {
				String nonceStr=getNonceStr(10);
				Long timestamp=DateUtil.getTimestamp();
				String string="jsapi_ticket="+ticket+"&noncestr="+nonceStr+"&timestamp="+timestamp+"&url="+url;
				LogUtil.Info("签名字符串string="+string);
				String signature =SHA1(string);
	            LogUtil.Info("WechatUtil.SHA1签名signature="+signature);
	            if (signature!=null&&signature.length()>0) {
	            	waChatConfig=new WaChatConfig(wechat_appid, timestamp, nonceStr, signature);
	            	LogUtil.Info("WaChatConfig="+waChatConfig);
				}
			}
		}
		return waChatConfig;
	}
	
	/**
	 * 获取AccessToken的tTicket
	 * @return
	 */
	public static String getTicket(){
		LogUtil.Info("getTicket");
		if (ticketAccessToken!=null&&ticketAccessToken.getExpires_in()!=null) {
			Long time=new Date().getTime()-ticketAccessToken.getGmtCreated().getTime();//计算时间间隔
			LogUtil.Info("getTicket");
			LogUtil.Info("计算时间间隔="+time/1000);
			if ((time/1000)<(ticketAccessToken.getExpires_in()-60)) {//过期时间比较
				return ticketAccessToken.getTicket();
			}
		}
		String urlStr="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+getAccessToken()+"&type=jsapi";
		String responseStr=wechatHttpUtil(urlStr, "POST", null, "获取AccessToken的tTicket");
		if(responseStr!=null&&responseStr.length()>0){
        	ticketAccessToken=JsonUtil.fromJson(responseStr, AccessToken.class);
        	if (ticketAccessToken!=null) {
        		ticketAccessToken.setGmtCreated(new Date());
        		return ticketAccessToken.getTicket();
			}
        }
		return "";
	}
	
	
	/**
	 * 小程序解密获取用户信息
	 * @param encryptedData
	 * @param sessionkey
	 * @param iv
	 * @return
	 */
	 public static WeChatMiniUserInfo getUserInfo(String encryptedData,String sessionkey,String iv){
		 if (encryptedData!=null&&encryptedData.length()>0&&sessionkey!=null&&sessionkey.length()>0&&iv!=null&&iv.length()>0) {
			 	// 被加密的数据
		        byte[] dataByte = Base64.decode(encryptedData);
		        // 加密秘钥
		        byte[] keyByte = Base64.decode(sessionkey);
		        // 偏移量
		        byte[] ivByte = Base64.decode(iv);
		        try {
		               // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
		            int base = 16;
		            if (keyByte.length % base != 0) {
		                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
		                byte[] temp = new byte[groups * base];
		                Arrays.fill(temp, (byte) 0);
		                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
		                keyByte = temp;
		            }
		            // 初始化
		            Security.addProvider(new BouncyCastleProvider());
		            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
		            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
		            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
		            parameters.init(new IvParameterSpec(ivByte));
		            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
		            byte[] resultByte = cipher.doFinal(dataByte);
		            if (null != resultByte && resultByte.length > 0) {
		                String result = new String(resultByte, "UTF-8");
		            	WeChatMiniUserInfo weChatUserInfo=JsonUtil.fromJson(result.toString(),WeChatMiniUserInfo.class);
		                return weChatUserInfo;
		            }
		        } catch (NoSuchAlgorithmException e) {
		            e.printStackTrace();
		        } catch (NoSuchPaddingException e) {
		            e.printStackTrace();
		        } catch (InvalidParameterSpecException e) {
		            e.printStackTrace();
		        } catch (IllegalBlockSizeException e) {
		            e.printStackTrace();
		        } catch (BadPaddingException e) {
		            e.printStackTrace();
		        } catch (UnsupportedEncodingException e) {
		            e.printStackTrace();
		        } catch (InvalidKeyException e) {
		            e.printStackTrace();
		        } catch (InvalidAlgorithmParameterException e) {
		            e.printStackTrace();
		        } catch (NoSuchProviderException e) {
		            e.printStackTrace();
		        }
		 } 
	     return null;
	  }
	
	/**
	 * SHA1签名
	 * @param decript
	 * @return
	 */
	public static String SHA1(String decript) {  
	    try {  
	        MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");  
	        digest.update(decript.getBytes());  
	        byte messageDigest[] = digest.digest();  
	        // Create Hex String  
	        StringBuffer hexString = new StringBuffer();  
	        // 字节数组转换为 十六进制 数  
	            for (int i = 0; i < messageDigest.length; i++) {  
	                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);  
	                if (shaHex.length() < 2) {  
	                    hexString.append(0);  
	                }  
	                hexString.append(shaHex);  
	            }  
	            return hexString.toString();  
	   
	        } catch (NoSuchAlgorithmException e) {  
	            e.printStackTrace();  
	        }  
	        return "";  
	}  
	
	
	/**
	 * 小程序支付签名
	 * @param packages  统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=wx2017033010242291fcfe0db70013231072
	 * @param nonceStr  
	 * @param timestamp
	 * @return
	 */
	public  static String createMiniSign(String packages,String nonceStr,String timestamp){
		SortedMap<String, String> signParam = new TreeMap<String, String>();
		signParam.put("appId", mini_appid);//app_id
		signParam.put("nonceStr", nonceStr);//自定义不重复的长度不长于32位
		signParam.put("timeStamp",new Date().getTime()+"");//北京时间时间戳
		signParam.put("package", packages);
		signParam.put("signType", "MD5");
		String sign = createSign(signParam);//再次生成签名
		return sign;
	}
	
	 //定义签名，微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
    public static String createSign(String prepayId,String nonceStr,String timestamp){
    	SortedMap<String, String> parameters = new TreeMap<String, String>();
    	parameters.put("appid", WechatUtil.phone_appid);//app_id
    	parameters.put("partnerid", WechatUtil.phone_mch_id);//微信商户账号
    	parameters.put("prepayid", prepayId);//预付订单id
    	parameters.put("package", "Sign=WXPay");//默认sign=WXPay
    	parameters.put("noncestr", nonceStr);//自定义不重复的长度不长于32位
    	parameters.put("timestamp",timestamp);//时间戳
        return createSign(parameters);
    }
	
	 //定义签名，微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
    private static String createSign(SortedMap<String,String> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + key);//最后加密时添加商户密钥，由于key值放在最后，所以不用添加到SortMap里面去，单独处理，编码方式采用UTF-8
        LogUtil.Info("签名前数据为="+sb.toString());
        String sign = MD5.getMD5(sb.toString()).toUpperCase();
        LogUtil.Info("生成app支付签名结束");
        return sign;
    }
	
	/**
	 * 获取随机字符串
	 * @param length
	 * @return
	 */
	public static String getNonceStr(Integer length) { //length表示生成字符串的长度  
	    String baseStr = "abcdefghijklmnopqrstuvwxyzQWERTYUIOPASDFGHJKLZXCVBNM0123456789";     
	    Random random = new Random();     
	    StringBuffer stringBuffer = new StringBuffer();     
	    for (int i = 0; i < length; i++) {     
	        int number = random.nextInt(baseStr.length());     
	        stringBuffer.append(baseStr.charAt(number));     
	    }     
	    return stringBuffer.toString();     
	 }
	
	 /** 
	   * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, 
	   * 
	   * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？ 
	   * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 
	   * 
	   * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130, 
	   * 192.168.1.100 
	   * 
	   * 用户真实IP为： 192.168.1.110 
	   * 
	   * @param request 
	   * @return 
	   */
	  public static String getIpAddress(HttpServletRequest request) { 
		    String ip = request.getHeader("x-forwarded-for"); 
		    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
		      ip = request.getHeader("Proxy-Client-IP"); 
		    } 
		    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
		      ip = request.getHeader("WL-Proxy-Client-IP"); 
		    } 
		    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
		      ip = request.getHeader("HTTP_CLIENT_IP"); 
		    } 
		    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
		      ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 
		    } 
		    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
		      ip = request.getRemoteAddr(); 
		    } 
		    return ip; 
	  }
	  
	  /**
	   * 创建微信提现sign
	   * @param characterEncoding	编码
	   * @param packageParams		参数
	   * @param API_KEY				密钥
	   * @return
	   */
	  public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
	        StringBuffer sb = new StringBuffer();
	        Set es = packageParams.entrySet();
	        Iterator it = es.iterator();

	        while(it.hasNext()) {
	            Entry entry = (Entry)it.next();
	            String k = entry.getKey().toString();
	            String v = entry.getValue().toString();
	            if (v != null && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
	                sb.append(k + "=" + v + "&");
	            }
	        }

	        sb.append("key=" + API_KEY);
	        String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
	        return sign;
	 }
	  
	public static String getKey() {
		return key;
	}
	public static String getNotifyUrl() {
		return notifyUrl;
	}
	public static void setNotifyUrl(String notifyUrl) {
		WechatUtil.notifyUrl = notifyUrl;
	}
	public static String getWechat_appid() {
		return wechat_appid;
	}
	public static void setWechat_appid(String wechat_appid) {
		WechatUtil.wechat_appid = wechat_appid;
	}
	public static String getPhone_appid() {
		return phone_appid;
	}
	public static void setPhone_appid(String phone_appid) {
		WechatUtil.phone_appid = phone_appid;
	}
	public static String getPhone_mch_id() {
		return phone_mch_id;
	}
	public static void setPhone_mch_id(String phone_mch_id) {
		WechatUtil.phone_mch_id = phone_mch_id;
	}
	public static String getWechat_mch_id() {
		return wechat_mch_id;
	}
	public static void setWechat_mch_id(String wechat_mch_id) {
		WechatUtil.wechat_mch_id = wechat_mch_id;
	}
	public static String getWechat_secret() {
		return wechat_secret;
	}
	public static void setWechat_secret(String wechat_secret) {
		WechatUtil.wechat_secret = wechat_secret;
	}
	public static String getWeb_appid() {
		return web_appid;
	}
	public static void setWeb_appid(String web_appid) {
		WechatUtil.web_appid = web_appid;
	}
	public static String getWeb_secret() {
		return web_secret;
	}
	public static void setWeb_secret(String web_secret) {
		WechatUtil.web_secret = web_secret;
	}
	public static Random getRandom() {
		return random;
	}
	public static void setRandom(Random random) {
		WechatUtil.random = random;
	}
	public static void setKey(String key) {
		WechatUtil.key = key;
	}
	public static String getMini_appid() {
		return mini_appid;
	}
	public static void setMini_appid(String mini_appid) {
		WechatUtil.mini_appid = mini_appid;
	}
	public static String getMini_secret() {
		return mini_secret;
	}
	public static void setMini_secret(String mini_secret) {
		WechatUtil.mini_secret = mini_secret;
	}
	
	public static String getMini_mch_id() {
		return mini_mch_id;
	}

	public static void setMini_mch_id(String mini_mch_id) {
		WechatUtil.mini_mch_id = mini_mch_id;
	}

	public static void main(String[] args) {
		String string="OPENID&DFDFDFDFD";
		string=string.replace("OPENID", "");
		System.out.println(string);
	}
	
	
}
